#include "TextRenderer.h"

#include <fstream>

#include <ezlogger_headers.hpp>

namespace Crows3D
{
   TextRenderer::TextRenderer(void)
   {
      EZLOGGERSTREAM << "TextRenderer Constructor" << std::endl;

      charData = NULL;
      texture  = NULL;
      effect   = NULL;

      lines.clear();

      numLines = 0;
   }

   TextRenderer::~TextRenderer(void)
   {
      EZLOGGERSTREAM << "TextRenderer Destructor" << std::endl;

      if (charData)
      {
         delete[] charData;
         charData = NULL;
      }

      for (unsigned int i = 0; i < lines.size(); i++)
      {
         if (lines[i]->vertexBuffer)
         {
            lines[i]->vertexBuffer->Release();
            lines[i]->vertexBuffer = NULL;
         }
         if (lines[i]->indexBuffer)
         {
            lines[i]->indexBuffer->Release();
            lines[i]->indexBuffer = NULL;
         }

         delete lines[i];
         lines[i] = NULL;
      }

      lines.clear();

      if (texture)
      {
         //delete texture;
         //texture = NULL;
      }

      if (effect)
      {
         //delete effect;
         //effect = NULL;
      }
   }

   bool TextRenderer::Initialize(Crows3D::Device *dev, std::string fontFile, Texture* fontTexture, EffectPosition2DTexture* fontEffect)
   {
      std::ifstream font;

      EZLOGGERSTREAM << "Initializing font data for rendering text" << std::endl;


      texture = fontTexture;
      effect = fontEffect;



      
      
      charData = new CharData[95];

      // Read in our font data from the file
      font.open(fontFile.c_str());
      if (font.fail())
      {
         return false;
      }

      // Read in the 95 used ascii characters for text.
	   for(int i = 0; i < 95; ++i)
	   {
         char temp;

         // skip the ascii code and char, they are there for easier reading
		   font.get(temp);
		   while(temp != ' ')
		   {
			   font.get(temp);
		   }
		   font.get(temp);
		   while(temp != ' ')
		   {
			   font.get(temp);
		   }

		   font >> charData[i].left;
		   font >> charData[i].right;
		   font >> charData[i].size;
	   }

      font.close();

      return true;
   }

   int TextRenderer::CreateLine(Crows3D::Device *dev)
   {
      EZLOGGERSTREAM << "Creating a line of text" << std::endl;

      TextLine* line = new TextLine();
      line->vertexBuffer = NULL;
      line->indexBuffer = NULL;
      line->numVerts = line->numIndices = 0;
      line->length = 0;
      line->red = line->green = line->blue = 1.0f;


      lines.push_back(line);



      this->InitializeBuffers(dev, (lines.size() - 1));

      return (int)(lines.size() - 1);
   }

   bool TextRenderer::DeleteLine(int index)
   {
      return true;
   }


   bool TextRenderer::UpdateLine(Crows3D::Device *dev, int index, std::string text, float x, float y, float screenWidth, float screenHeight)
   {
      Vertex2DPositionTexture* verts;
      unsigned long* indices;

      // Adjust coordinates
      x = (-(screenWidth / 2)) + x;
      y = (screenHeight / 2) - y;

      // Get string length and number of verts/inidices necessary
      lines[index]->length     = text.length();
      lines[index]->numVerts   = 6 * lines[index]->length;
      lines[index]->numIndices = 6 * lines[index]->length;

      // intialize our vertex and index arrays
      verts    = new Vertex2DPositionTexture[lines[index]->numVerts];
      indices  = new unsigned long[lines[index]->numIndices];

      memset(verts, 0, (sizeof(Vertex2DPositionTexture) * lines[index]->numVerts));

      // Set our indices, they are just sequential
	   for(int i = 0; i < lines[index]->numIndices; ++i)
	   {
		   indices[i] = i;
	   }

      // Set our vertex data
      for (int i = 0; i < lines[index]->length; ++i)
      {
         int letter = text[i] - 32; // We dont handle first 32 chars, these are special (system) related

         if (letter == 0)
         {
            // No geometry needed for a space, lets save the 6 tris
            x += charData[letter].size;
            continue;
         }

         // Top Left
         verts[i * 6 + 0].position = D3DXVECTOR4(x, y, 0.0f, 1.0f);
         verts[i * 6 + 0].texture  = D3DXVECTOR2(charData[letter].left, 0.0f);

         // Bottom Right
         verts[i * 6 + 1].position = D3DXVECTOR4((x + charData[letter].size), (y - 16), 0.0f, 1.0f);
         verts[i * 6 + 1].texture  = D3DXVECTOR2(charData[letter].right, 1.0f);

         // Bottom Left
         verts[i * 6 + 2].position = D3DXVECTOR4(x, (y - 16), 0.0f, 1.0f);
         verts[i * 6 + 2].texture  = D3DXVECTOR2(charData[letter].left, 1.0f);

         // Top Left
         verts[i * 6 + 3].position = D3DXVECTOR4(x, y, 0.0f, 1.0f);
         verts[i * 6 + 3].texture  = D3DXVECTOR2(charData[letter].left, 0.0f);

         // Top Right
         verts[i * 6 + 4].position = D3DXVECTOR4((x + charData[letter].size), y, 0.0f, 1.0f);
         verts[i * 6 + 4].texture  = D3DXVECTOR2(charData[letter].right, 0.0f);

         // Bottom Right
         verts[i * 6 + 5].position = D3DXVECTOR4((x + charData[letter].size), (y - 16), 0.0f, 1.0f);
         verts[i * 6 + 5].texture  = D3DXVECTOR2(charData[letter].right, 1.0f);

         // Move over to the next vertex poisition
         x += (charData[letter].size + 1.0f);
      }

      // Pointer to vertex data we will be writing
      D3D11_MAPPED_SUBRESOURCE mappedResource;

      // We now have our verts and indices ready to load into the vb and ib
      if (FAILED(dev->GetDeviceContext()->Map(lines[index]->vertexBuffer, 0, D3D11_MAP_WRITE_DISCARD, NULL, &mappedResource)))
      {
         return false;
      }
      else
      {
         // Copy in our data
         memcpy(mappedResource.pData, verts, sizeof(Vertex2DPositionTexture) * lines[index]->numVerts);

         dev->GetDeviceContext()->Unmap(lines[index]->vertexBuffer, 0);
      }

      if (FAILED(dev->GetDeviceContext()->Map(lines[index]->indexBuffer, 0, D3D11_MAP_WRITE_DISCARD, NULL, &mappedResource)))
      {
         return false;
      }
      else
      {
         // Copy in our data
         memcpy(mappedResource.pData, indices, sizeof(unsigned long) * lines[index]->numIndices);

         dev->GetDeviceContext()->Unmap(lines[index]->indexBuffer, 0);
      }




      delete [] verts;
      verts = NULL;
      
      delete [] indices;
      indices = NULL;

      return true;
   }

   
   bool TextRenderer::InitializeBuffers(Crows3D::Device *dev, int index)
   {
      D3D11_BUFFER_DESC vertexBufferDesc, indexBufferDesc;

      EZLOGGERSTREAM << "Creating vertex/index buffer to render a line of text" << std::endl;

      // Set up the description of the dynamic vertex buffer.
      vertexBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
      vertexBufferDesc.ByteWidth = sizeof(Vertex2DPositionTexture) * MAX_CHARACTERS_PER_LINE * 6;
      vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
      vertexBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
      vertexBufferDesc.MiscFlags = 0;
	   vertexBufferDesc.StructureByteStride = 0;    

	   if(FAILED(dev->GetDevice()->CreateBuffer(&vertexBufferDesc, NULL, &lines[index]->vertexBuffer)))
	   {
         EZLOGGERSTREAM << "ERROR: Failed to create vertex buffer for text" << std::endl;

		   return false;
	   }


      // Set up the description of the static index buffer.
      indexBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
      indexBufferDesc.ByteWidth = sizeof(unsigned long) * MAX_CHARACTERS_PER_LINE * 6;
      indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
      indexBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
      indexBufferDesc.MiscFlags = 0;
	   indexBufferDesc.StructureByteStride = 0;

	   // Create the index buffer.
	   if(FAILED(dev->GetDevice()->CreateBuffer(&indexBufferDesc, NULL, &lines[index]->indexBuffer)))
	   {
         EZLOGGERSTREAM << "ERROR: Failed to create index buffer for text" << std::endl;

		   return false;
	   }

      return true;
   }
   
   
   
   
   
   
   void TextRenderer::RenderLines(Crows3D::Device *dev, Crows3D::Camera* cam)
   {
      unsigned int stride, offset;

      D3DXMATRIX world, view, proj;

      // Get our matrices
      D3DXMatrixIdentity(&world);
      cam->GetMatrices(view, proj);

      if (!effect || !texture)
      {
         return;
      }

      effect->Commit(dev, texture, world, view, proj);


      // Render vertex buffers
      stride = sizeof(Vertex2DPositionTexture);
      offset = 0;

      for (int index = 0; index < (int)lines.size(); ++index)
      {
         // Make sure the buffers were created
         if ((!lines[index]->vertexBuffer) || (!lines[index]->indexBuffer))
         {
            return;
         }

         dev->GetDeviceContext()->IASetVertexBuffers(0, 1, &lines[index]->vertexBuffer, &stride, &offset);
         dev->GetDeviceContext()->IASetIndexBuffer(lines[index]->indexBuffer, DXGI_FORMAT_R32_UINT, 0);

         // Tell the device it'll be a list of triangles
         dev->GetDeviceContext()->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

         // Render!
         dev->GetDeviceContext()->DrawIndexed(lines[index]->numIndices, 0, 0);
      }

   }


}